package top.whysu.edevice.service.ezviz.impl;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import top.whysu.edevice.constant.CodeConstant;
import top.whysu.edevice.constant.DataDictionary;
import top.whysu.edevice.constant.EzvizConstant;
import top.whysu.edevice.dao.ezviz.EzvizDao;
import top.whysu.edevice.dao.relative.RelativeVoucherAccountDao;
import top.whysu.edevice.dto.ezviz.child.ChildTokenRequestDTO;
import top.whysu.edevice.dto.ezviz.child.ChildTokenResponseDTO;
import top.whysu.edevice.dto.ezviz.policy.EzvizPolicy;
import top.whysu.edevice.dto.ezviz.policy.EzvizStatement;
import top.whysu.edevice.dto.ezviz.token.TokenDTO;
import top.whysu.edevice.dto.ezviz.token.TokenRequestDTO;
import top.whysu.edevice.dto.ezviz.token.TokenResponseDTO;
import top.whysu.edevice.dto.ezviz.token.TokenWithAccountDTO;
import top.whysu.edevice.exception.BusinessException;
import top.whysu.edevice.po.manage.ManageDevicePO;
import top.whysu.edevice.po.relative.RelativeVoucherAccountPO;
import top.whysu.edevice.service.ezviz.EzvizHttpService;
import top.whysu.edevice.service.ezviz.EzvizTokenService;
import top.whysu.edevice.service.relative.RelativeSecretKeyTokenService;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service("EzvizTokenServiceImpl")
public class EzvizTokenServiceImpl implements EzvizTokenService {

    private static Logger LOGGER = LoggerFactory.getLogger(EzvizTokenServiceImpl.class);

    @Resource
    private EzvizDao ezvizDao;
    @Resource
    private EzvizHttpService ezvizHttpService;
    @Resource
    private RelativeSecretKeyTokenService relativeSecretKeyTokenService;
    @Resource
    private RelativeVoucherAccountDao relativeVoucherAccountDao;

    @Override
    public TokenWithAccountDTO getToken(BigDecimal accountId, BigDecimal voucherId) {
        if (accountId == null || voucherId == null) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "账号id和凭证组id不能为空！");
        }
        TokenWithAccountDTO tokenDTO = ezvizDao.getTokenByAccountId(accountId, voucherId);
        if (tokenDTO == null) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "当前账号未关联凭证组，或者凭证组未关联秘钥！");
        }
        if (tokenDTO.getExpireTime() == null || System.currentTimeMillis() > tokenDTO.getExpireTime()) {
            // 如果过期的话重新获取accessToken并更新
            TokenResponseDTO tokenResponseDTO = refreshAccessToken(tokenDTO.getAppKey(), tokenDTO.getAppSecret(), tokenDTO.getSecretKeyId());
            // 返回的值也更新
            tokenDTO.setAccessToken(tokenResponseDTO.getAccessToken());
            tokenDTO.setExpireTime(tokenResponseDTO.getExpireTime());
        }
        return tokenDTO;
    }

    @Override
    public List<TokenWithAccountDTO> getTokenAccountList(BigDecimal voucherId) {
        if (voucherId == null) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "凭证组id不能为空！");
        }
        List<TokenWithAccountDTO> tokenAccountList = ezvizDao.getTokenAccountList(voucherId);
        if (CollectionUtils.isEmpty(tokenAccountList)) {
            LOGGER.error("当前凭证组id下未关联秘钥或账号，voucherId: {}", voucherId);
            return null;
        }
        // 选取其中一条的查看Token是否过期
        TokenWithAccountDTO tokenDTO = tokenAccountList.get(0);
        if (tokenDTO.getExpireTime() == null || System.currentTimeMillis() > tokenDTO.getExpireTime()) {
            // 如果过期的话重新获取accessToken并更新
            TokenResponseDTO tokenResponseDTO = refreshAccessToken(tokenDTO.getAppKey(), tokenDTO.getAppSecret(), tokenDTO.getSecretKeyId());
            // 更新集合里面对应的值
            tokenAccountList.forEach(it -> {
                it.setAccessToken(tokenResponseDTO.getAccessToken());
                it.setExpireTime(tokenResponseDTO.getExpireTime());
            });
        }
        return tokenAccountList;
    }

    @Override
    public TokenWithAccountDTO getChildAccountToken(BigDecimal accountId) {
        if (accountId == null) {
            throw new BusinessException(CodeConstant.Http.PARAM_ERROR, "账号id不能为空");
        }
        // 获取凭证组id
        RelativeVoucherAccountPO queryPO = new RelativeVoucherAccountPO();
        queryPO.setAccountId(accountId);
        List<RelativeVoucherAccountPO> list = relativeVoucherAccountDao.queryAll(queryPO);
        if (CollectionUtils.isEmpty(list)) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "当前账号未关联凭证组！");
        }
        // 获取token信息
        TokenWithAccountDTO tokenWithAccountDTO = this.getToken(accountId, list.get(0).getVoucherId());
        // 获取子账号accessToken
        ChildTokenRequestDTO childTokenRequestDTO = new ChildTokenRequestDTO();
        childTokenRequestDTO.setAccessToken(tokenWithAccountDTO.getAccessToken());
        childTokenRequestDTO.setAccountId(tokenWithAccountDTO.getEzvizAccountId());
        ChildTokenResponseDTO childTokenResponseDTO = ezvizHttpService.getChildAccessToken(childTokenRequestDTO);
        // 封装
        tokenWithAccountDTO.setChildAccessToken(childTokenResponseDTO.getAccessToken());
        tokenWithAccountDTO.setChildExpireTime(childTokenResponseDTO.getExpireTime());
        return tokenWithAccountDTO;
    }

    @Override
    public TokenDTO getTokenByVoucherId(BigDecimal voucherId) {
        if (voucherId == null) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "凭证组id不能为空！");
        }
        TokenDTO tokenDTO = ezvizDao.getTokenByVoucherId(voucherId);
        if (tokenDTO == null) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "凭证组未关联秘钥！");
        }
        if (tokenDTO.getExpireTime() == null || System.currentTimeMillis() > tokenDTO.getExpireTime()) {
            // 如果过期的话重新获取accessToken并更新
            TokenResponseDTO tokenResponseDTO = refreshAccessToken(tokenDTO.getAppKey(), tokenDTO.getAppSecret(), tokenDTO.getSecretKeyId());
            // 返回的值也更新
            tokenDTO.setAccessToken(tokenResponseDTO.getAccessToken());
            tokenDTO.setExpireTime(tokenResponseDTO.getExpireTime());
        }
        return tokenDTO;
    }

    @Override
    public String ezvizPassword(String currentPassword, String appKey) {
        String str = appKey + "#" + currentPassword;
        return DigestUtils.md5DigestAsHex(str.getBytes()).toLowerCase();
    }

    @Override
    public EzvizPolicy getPolicy(List<ManageDevicePO> devicePOList, String roleSign) {
        EzvizPolicy ezvizPolicy = new EzvizPolicy();
        List<EzvizStatement> statementList = new ArrayList<>();
        statementList.add(this.getStatement(devicePOList, roleSign));
        ezvizPolicy.setStatement(statementList);
        return ezvizPolicy;
    }

    @Override
    public EzvizStatement getStatement(List<ManageDevicePO> devicePOList, String roleSign) {
        EzvizStatement statement = new EzvizStatement();
        if (DataDictionary.RoleSign.SUPER_ADMIN.equals(roleSign)) {
            // 超级管理员（给所有权限）
            statement.setPermission(getPermissionStr(
                    EzvizConstant.Permission.UPDATE,
                    EzvizConstant.Permission.GET,
                    EzvizConstant.Permission.REAL,
                    EzvizConstant.Permission.REPLAY,
                    EzvizConstant.Permission.CAPTURE,
                    EzvizConstant.Permission.VIDEO,
                    EzvizConstant.Permission.PTZ,
                    EzvizConstant.Permission.PTZ,
                    EzvizConstant.Permission.UPGRADE,
                    EzvizConstant.Permission.FORMAT,
                    EzvizConstant.Permission.PIPE,
                    EzvizConstant.Permission.DEV_CTRL));
        } else if (DataDictionary.RoleSign.ADMIN.equals(roleSign)) {
            // 管理员
            statement.setPermission(getPermissionStr(
                    EzvizConstant.Permission.UPDATE,
                    EzvizConstant.Permission.GET,
                    EzvizConstant.Permission.REAL,
                    EzvizConstant.Permission.REPLAY,
                    EzvizConstant.Permission.CAPTURE,
                    EzvizConstant.Permission.VIDEO,
                    EzvizConstant.Permission.CONFIG));
        } else if (DataDictionary.RoleSign.VISITOR.equals(roleSign)) {
            // 访客
            statement.setPermission(getPermissionStr(
                    EzvizConstant.Permission.GET,
                    EzvizConstant.Permission.REAL,
                    EzvizConstant.Permission.REPLAY,
                    EzvizConstant.Permission.VIDEO,
                    EzvizConstant.Permission.CAPTURE));
        }
        // resourceType: 表示资源类型，现在只有2种资源类型，dev（设备）、cam（通道）
        // 表示资源Id，dev类型的resourceId是设备序列号，cam类型的resourceId是设备序列号:通道号
        // 例如"Resource": ["dev:469631729","cam:544229080:1"]
        List<String> resoureList = new ArrayList<>();
        devicePOList.forEach(it -> {
            if (StringUtils.isNotBlank(it.getDeviceSerial())) {
                if (DataDictionary.DeviceType.IPC == it.getDeviceType()) {
                    resoureList.add("cam:" + it.getDeviceSerial() + ":1");
                } else if (DataDictionary.DeviceType.COLLECT == it.getDeviceType()) {
                    resoureList.add("dev:" + it.getDeviceSerial());
                }
            }
        });
        statement.setResource(resoureList);
        return statement;
    }

    /**
     * 将多个persmission用逗号合起来。例如："Get,Update,DevCtrl"
     */
    private String getPermissionStr(String... permission) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < permission.length; i++) {
            if (i == permission.length - 1) {
                stringBuilder.append(permission[i]);
            } else {
                stringBuilder.append(permission[i]).append(",");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 重新获取accessToken并更新
     */
    private TokenResponseDTO refreshAccessToken(String appKey, String appSecret, BigDecimal secretKeyId) {
        // 过期的话，要重新获取
        TokenRequestDTO tokenRequestDTO = new TokenRequestDTO();
        tokenRequestDTO.setAppKey(appKey);
        tokenRequestDTO.setAppSecret(appSecret);
        TokenResponseDTO tokenResponseDTO = ezvizHttpService.getAccessToken(tokenRequestDTO);
        // 更新秘钥对应的token
        relativeSecretKeyTokenService.wholeSecretKeyToken(secretKeyId, tokenResponseDTO.getAccessToken(), tokenResponseDTO.getExpireTime());
        return tokenResponseDTO;
    }
}
